package com.jweb.framework.ws.sdo;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;

import org.springframework.oxm.Marshaller;
import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.oxm.XmlMappingException;
import org.springframework.util.Assert;
import org.springframework.xml.transform.TraxUtils;

import com.jweb.framework.core.support.TRACER;
import com.jweb.framework.core.util.XMLUtils;
import com.jweb.framework.ws.channel.SchemaValidator;
import com.jweb.framework.ws.channel.ServiceManager;
import com.jweb.framework.ws.channel.ServiceManagerAware;
import com.jweb.framework.ws.support.DelegatingStreamWriter;

import commonj.sdo11.DataObject;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：MAP键值对与SOAP报文之间相互转换
 */
public class SdoMarshaller implements ServiceManagerAware , Marshaller , Unmarshaller {
	
	public static final String DEFAULT_REQUEST_SUFFIX = "Request";
	public static final String DEFAULT_RESPONSE_SUFFIX = "Response";
	
	private String requestSuffix = "Request";
	private String responseSuffix = "Response";
	
	private ServiceManager serviceManager;
	private SdoConverter sdoConverter ;
	
	private boolean serverSide ;
	private boolean validation ;
	private boolean validationOnMarshalling ;
	
	public void setServerSide(boolean flag){
		this.serverSide = flag ;
	}
	
	public void setValidation(boolean flag){
		this.validation = flag  ;
	}
	
	public void setValidationOnMarshalling(boolean flag){
		this.validationOnMarshalling = flag ;
	}
	
	public void setServiceManager(ServiceManager servicemanager) {
		this.serviceManager = servicemanager ;
	}
	
	public void setRequestSuffix(String str){
		Assert.hasText(str , "'requestSuffix' must not be empty");
		this.requestSuffix = str ;
	}
	
	public void setResponseSuffix(String str){
		Assert.hasText(str , "'responseSuffix' must not be empty");
		this.responseSuffix = str ;
	}
	
	public void setConverter(SdoConverter sdoConverter){
		this.sdoConverter = sdoConverter ;
	}
	
	/**
	 * 键值对MAP转换成SOAP
	 */
	public void marshal(Object obj , Result result) throws IOException , XmlMappingException {
		Map map = (Map)obj ;
		if(map == null){
			return ;
		}
		QName qname = (QName)map.remove("QNameMapping.current");
		if(this.serverSide){
			String s = qname.getLocalPart();
			if(s.endsWith(this.requestSuffix)){
				s = s.substring(0, s.length()-this.requestSuffix.length());
			}
			qname = new QName(qname.getNamespaceURI() , (new StringBuilder(String.valueOf(s))).append(this.responseSuffix).toString());
		}
		
		String s1 = this.serviceManager.getServiceId(qname); //s1="actDateService"
		if(s1 == null){
			throw new IllegalStateException((new StringBuilder("service id is null for ")).append(qname).toString());
		}
		
		DataObject dataObject = SdoUtils.createDataObject(s1, qname);
		this.sdoConverter.toSdo(map, dataObject);
		
		try {
			
			Object obj1 ;
			//if(!this.validationOnMarshalling && StaxUtils.isStaxResult(result)){
			if(!this.validationOnMarshalling && TraxUtils.isStaxResult(result)){ //false
				obj1 = TraxUtils.getXMLStreamWriter(result);
			}
			else {
				obj1 = new DelegatingStreamWriter();
			}
			SdoUtils.save(s1, qname, dataObject, (XMLStreamWriter) obj1); // 将dataObject写入到XMLStreamWriter
			((XMLStreamWriter)obj1).flush(); // flush()将所有缓存数据写入底层输出机制。
			if(this.validationOnMarshalling){ // true
				DelegatingStreamWriter delegatingStreamWriter = (DelegatingStreamWriter) obj1 ;
				SchemaValidator schemaValidator = this.serviceManager.getValidator(qname);
				if(schemaValidator != null){ //StreamSource:以XML标记流的形式充当转换Source的持有者。
					StreamSource streamSource = new StreamSource(new ByteArrayInputStream(delegatingStreamWriter.getBytes()));
					
					if(!schemaValidator.validate(streamSource)){
						throw new MarshallingFailureException("xsd validation error .");
					}
				}
			}
			
			if(obj1 instanceof DelegatingStreamWriter){ // true
				DelegatingStreamWriter delegatingStreamWriter1 = (DelegatingStreamWriter) obj1 ;
				XMLUtils.transform(delegatingStreamWriter1.getSource(), result);
			}
			
		} catch (Exception e) {
			throw new MarshallingFailureException("sdo marshing error" , e);
		}
		
	}
	
	public boolean supports(Class<?> clazz) {
		
		return Map.class.isAssignableFrom(clazz) ;
		
	}
	
	public Object unmarshal(Source source) throws IOException,XmlMappingException {
		
		if(TraxUtils.isStaxSource(source)){
			
			/*
			 * XMLStreamReader 接口允许转发XML和对XML的只读访问。它被设计为读取XML数据的最低层且最有效的方法。
			 */
			XMLStreamReader xmlStreamReader = TraxUtils.getXMLStreamReader(source);
			try {
				
				/*
				 * StreamSource ： 以XML标记流的形式充当转换Source的持有者。
				 */
				StreamSource streamSource = null ;
				
				// 特殊报文处理 start
				byte[] soapBytes = XMLUtils.toBytes(source);
				xmlStreamReader = XMLUtils.createXMLReader(new ByteArrayInputStream(soapBytes) , null  , false);
				streamSource = new StreamSource(new ByteArrayInputStream(soapBytes));
				// 特殊报文处理 end
				
				
				if(this.validation){
					/*byte bytes[] = XMLUtils.toBytes(source);
					xmlStreamReader = XMLUtils.createXMLReader(new ByteArrayInputStream(bytes) , null  , false);
					streamSource = new StreamSource(new ByteArrayInputStream(bytes));*/
				}
				
				while(xmlStreamReader.next() != 1) ;
				
				/*
				 * QName getName()：返回当前START_ELEMENT 或 END_ELEMENT 事件的QName。
				 */
				QName qname = xmlStreamReader.getName();//qname={http://gsp.service.bankcomm.com/abstraction/atomic/technical.QueryAcctDateService/1_0}queryAcctDateResponse
				//qname={http://gsp.bankcomm.com}responseType
				if(this.validation){
				 //if(true){	
					
					SchemaValidator schemaValidator = serviceManager.getValidator(qname);
					if(schemaValidator != null && !schemaValidator.validate(streamSource)){
					//if(schemaValidator != null && !schemaValidator.validate(source)){
						throw new UnmarshallingFailureException("xsd validation error.");
					}
				}
				
				String s = serviceManager.getServiceId(qname);//s="ActDateService"
				
				if(s == null){
					throw new IllegalStateException((new StringBuilder("service id is null for")).append(qname).toString());
				}
				
				Map map = new HashMap() ;
				try {
					DataObject dataObject = SdoUtils.load(s, xmlStreamReader);
					map = this.sdoConverter.toData(dataObject);
					
					/** 处理资金系统CDATA特殊报文start */
					if(map.containsKey("cemType") && map.containsValue("CEM")){
						if(map.containsKey("cemIn")){
							String fundIn = new String(soapBytes , "UTF-8");
							fundIn = fundIn.substring(fundIn.indexOf("<![CDATA["), fundIn.indexOf("]]>")+3);
							map.put("cemIn", fundIn);
						}
					}
					/** 处理资金系统CDATA特殊报文end */
					
				} catch (Exception e) {
					// 特殊报文直接拿出处理
					String gxSoap = new String(soapBytes , "UTF-8");
					map.put("gxSoap",  gxSoap) ;
					
					TRACER.trace("gx soap parser ..." );
				}
				
				if(this.serverSide){
					map.put("QNameMapping.current", qname);
					if(this.serviceManager.isOneway(qname)){
						map.put("ServiceManager.Oneway", qname);
					}
				}
				
				return map ;
				
			} catch (XMLStreamException e) {
				
				throw new UnmarshallingFailureException("sdo unmarshing error" , e);
				
			} catch (TransformerException te){
				
				throw new UnmarshallingFailureException("sdo unmarshing error" , te);
			}
		}
		else {
			throw new UnsupportedOperationException("just support StaxSource");
		}
	}
	
}
